#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <openssl/md5.h>
#include <openssl/sha.h>
#include <sys/sysinfo.h>
#include "filename_cache.h"
#include "url_info.h"

int url_sha1(unsigned char *buff, struct url_info *url);

#define sha1_to_longlong(sha1) (*(unsigned long long*)sha1)
struct fcache_unit
{
	struct hkey key;
	struct list_head link;
	u_int32_t ip;
} __attribute__ ((packed));

void* refresh_mava(void *parm)
{
	struct filename_cache *fc = (struct filename_cache*)parm;
	struct sysinfo info;
	while (1)
	{
		if (sysinfo(&info) == 0)
		{
			fc->mava = ((long long)info.freeram)*info.mem_unit/1024/1024;
		}
		sleep(30);
	}
	return 0;
}
int fcache_init(struct filename_cache *fc)
{
	fc->num = 0;
	if (htable_init(&fc->htable) < 0)
		return -1;
	INIT_LIST_HEAD(&fc->newly);
	fc->mava = 1024;
	if (pthread_mutex_init(&fc->lock, 0) != 0)
		return -1;
	if (pthread_create(&fc->t, 0, refresh_mava, fc) != 0)
		return -1;
	return 1;
}

void p(struct filename_cache *fc)
{
	int i;
	struct list_head *head;
	struct list_head *cur;
	for (i=0; i<HTABLE_SIZE; i++)
	{
		head = &fc->htable.header[i];
		cur = head->next;
		if (cur != head)
			printf("\n");
		while (cur != head)
		{
			printf("%X ", cur);
			cur = cur->next;
		}
	}
	printf("\n");
	printf("==\n");
	head = &fc->newly;
	cur = head->next;
	while (cur != head)
	{
		printf("%X ", cur);
		cur = cur->next;
	}
	
	printf("\n\n");
}
struct fcache_unit* __alloc_unit(struct filename_cache *fc)
{
	void *m;
	struct list_head *lu_head;
	struct fcache_unit *lu_unit;
	struct fcache_unit *unit;

	if (fc->mava  > 50) {
		m = malloc(sizeof(struct fcache_unit));
		if (m)
			return m;
	}

	lu_head = fc->newly.prev;
	if (lu_head == &fc->newly)
		return 0;
	list_del(lu_head);
	unit = container_of(lu_head, struct fcache_unit, link);
	htable_del(&fc->htable, &unit->key);
	return unit;
}
int fcache_add(struct filename_cache *fc, struct url_info *url, u_int32_t ip)
{
	int ret;
	struct fcache_unit *unit;
	char sha1[20];

	if (!url_sha1(sha1, url))
		return -1;

	pthread_mutex_lock(&fc->lock);
	if ((unit = __alloc_unit(fc)))
	{
		make_key(&unit->key, sha1_to_longlong(sha1));
		unit->ip = ip;
		htable_add(&fc->htable, &unit->key);
		list_add(&unit->link, &fc->newly);
	}
	pthread_mutex_unlock(&fc->lock);
	return unit ? 1 : -1;
}

int fcache_find(struct filename_cache *fc, struct url_info *url, u_int32_t *ip)
{
	int ret;
	struct fcache_unit *unit;
	struct hkey *finded, key;
	char sha1[20];

	if (!url_sha1(sha1, url))
		return -1;

	make_key(&key, sha1_to_longlong(sha1));

	pthread_mutex_lock(&fc->lock);
	finded = htable_find(&fc->htable, &key);
	if (finded)
	{
		unit = container_of(finded, struct fcache_unit, key);
		*ip = unit->ip;
		list_del(&unit->link);
		list_add(&unit->link, &fc->newly);
	}
	pthread_mutex_unlock(&fc->lock);
	return finded != 0;
}

int fcache_update(struct filename_cache *fc, struct url_info *url, u_int32_t ip)
{
	int ret;
	struct fcache_unit *unit;
	struct hkey *finded, key;
	char sha1[20];

	if (!url_sha1(sha1, url))
		return -1;

	make_key(&key, sha1_to_longlong(sha1));

	pthread_mutex_lock(&fc->lock);
	finded = htable_find(&fc->htable, &key);
	if (finded)
	{
		unit = container_of(finded, struct fcache_unit, key);
		unit->ip = ip;
		list_del(&unit->link);
		list_add(&unit->link, &fc->newly);
	}
	pthread_mutex_unlock(&fc->lock);
	return finded != 0;

}

int _main()
{
	u_int32_t ip;
	struct url_info url, url2;
	struct filename_cache fc;

	url.url = url.buff;
	url2.url = url2.buff;

	strcpy(url.url, "http://www.baidu.com/ffff.mp4");
	locate_filename_and_qmark(&url);

	strcpy(url2.url, "http://www.baidu.com/mm.zip");
	locate_filename_and_qmark(&url2);

	fcache_init(&fc);
	
	fcache_add(&fc, &url, 45);
	p(&fc);


	fcache_add(&fc, &url2, 46);
	p(&fc);

	if (fcache_update(&fc, &url, 78))
		printf("yes: \n");
	else printf("no\n");

	if (fcache_find(&fc, &url, &ip))
		printf("yes: %d\n", ip);
	else printf("no\n");
	if (fcache_find(&fc, &url2, &ip))
		printf("yes: %d\n", ip);
	else printf("no\n");

	sleep(30);
	return 0;
}

/* buff must at least SHA_DIGEST_LENGTH == 20 bytes */
void do_sha1(unsigned char *buff, char *name, int len, char *host, int hostlen)
{
	SHA_CTX sh1;
	SHA1_Init(&sh1);
	SHA1_Update(&sh1, name, len);
	SHA1_Update(&sh1, host, hostlen);
	SHA1_Final(buff, &sh1);
}

/* buff must at least SHA_DIGEST_LENGTH == 20 bytes */
int url_sha1(unsigned char *buff, struct url_info *url)
{
	int i1, i2, i3, i4;
	unsigned char none = 1;
	char *dot = index(url->url + 7, '.');
	char *slash = index(url->url + 7, '/');
	if (sscanf(url->url + 7, "%u.%u.%u.%u", &i1, &i2, &i3, &i4) == 4) {
		do_sha1(buff, url->filename, url->filenamelen, &none, 1);
		return 1;
	}
	if (dot && slash && slash > (dot + 1)) {
		dot ++;
		do_sha1(buff, url->filename, url->filenamelen, dot, slash - dot);
		return 1;
	}
	return 0;
}

